#include "qrcodedetect.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <QDebug>

QrcodeDetect::QrcodeDetect()
{

}

int QrcodeDetect::detectQrcode(QString filePath)
{
      Mat src = imread( filePath.toStdString(), 1 );//不需要手动释放？
      return detectQrcode(src);
}

int QrcodeDetect::detectQrcode(Mat srcImage)
{
    //二维码的外框线细了点，加粗，提高鲁棒性。
    int returnFlag=0;
//    cv::resize(srcImage,srcImage,Size(800,600));//标准大小,不能修改大小，否则不清晰了
    Mat src_gray;
    Mat src_all=srcImage.clone();
    Mat threshold_output;
    vector<vector<Point> > contours,contours2;
    vector<Vec4i> hierarchy;
    //预处理
    cvtColor( srcImage, src_gray, CV_BGR2GRAY );
    imshow( "灰度", src_gray );
//    blur( src_gray, src_gray, Size(3,3) ); //模糊，去除毛刺
//    imshow( "去毛刺", src_gray );
    //https://blog.csdn.net/u012566751/article/details/77046445 THRESH_OTSU为自动选择阀值
//    threshold( src_gray, threshold_output, 100, 255, THRESH_OTSU );//可以自适应灯光的影响。
    threshold( src_gray, threshold_output, 200, 255, THRESH_BINARY );//值越大代表白色。考虑灯光的影响。
    imshow( "二值化", threshold_output );
    //寻找轮廓
    //第一个参数是输入图像 2值化的
    //第二个参数是内存存储器，FindContours找到的轮廓放到内存里面。
    //第三个参数是层级，**[Next, Previous, First_Child, Parent]** 的vector
    //第四个参数是类型，采用树结构
    //第五个参数是节点拟合模式，这里是全部寻找
    findContours( threshold_output, contours, hierarchy,  CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0) );
    //轮廓筛选
    int ic=0;
    int parentIdx=-1;
    double allowMinArea=0;
    double allowMaxArea=5000;
    double allowMinLength=0;
    double allowMaxLength=100;
    //不允许二维码外部再被一个方框包裹
    for( int i = 0; i< contours.size(); i++ )
    {

        //hierarchy[i][2] != -1 表示不是最外面的轮廓
        if (hierarchy[i][2] != -1 && ic==0)
        {
            parentIdx = i;
            ic++;
        }
        else if (hierarchy[i][2] != -1)
        {
            ic++;
        }
        //最外面的清0
        else if(hierarchy[i][2] == -1)
        {
            ic = 0;
            parentIdx = -1;
        }
        //找到定位点信息
        if ( ic >= 2)
        {
//                //判断方框大小是否符合要求
//                double area = contourArea(contours[i]);
//                if (area < allowMinArea || area >allowMaxArea)
//                {
//                    continue;
//                }
//                //判断方框边长是否符合要求
//                RotatedRect rect = minAreaRect(contours[i]);
//                double w = rect.size.width;
//                double h = rect.size.height;
//                if (w < allowMinLength || w >allowMaxLength || h < allowMinLength || h >allowMaxLength )
//                {
//                    continue;
//                }

            contours2.push_back(contours[parentIdx]);
            ic = 0;
            parentIdx = -1;
        }
    }
    //填充定位点
    for(int i=0; i<contours2.size(); i++)
        drawContours( src_all, contours2, i,  CV_RGB(0,255,0) , -1 );
    //连接定位点
    Point point[3];
    for(int i=0; i<contours2.size(); i++)
    {
        point[i] = center_cal( contours2, i );
    }

    line(src_all,point[0],point[1],Scalar(0,0,255),2);
    line(src_all,point[1],point[2],Scalar(0,0,255),2);
    line(src_all,point[0],point[2],Scalar(0,0,255),2);
    QString showMsg;
    if(3==contours2.size())
    {
        double tmpX,tmpY,tmpR;
        calculateQrcodeLocation(contours2,tmpX,tmpY,tmpR);
        showMsg="sucess x=";
        showMsg+=QString::number(tmpX,'g',4);
        showMsg+=" ,y="+QString::number(tmpY,'g',4);
        showMsg+=" ,r="+QString::number(tmpR,'g',4);
        returnFlag=1;
    }
    else
    {
        showMsg="failed ,contours2.size()=";
        showMsg+=QString::number(contours2.size());
        returnFlag=-1;
    }

    putText(src_all,showMsg.toStdString(),Point(20,25),CV_FONT_HERSHEY_SIMPLEX,1,Scalar(255,100,0),2,2);

    imshow( "结果", src_all );
    return returnFlag;

}

Point QrcodeDetect::center_cal(vector<vector<Point> > contours, int i)
{

    //找到所提取轮廓的中心点
    //在提取的中心小正方形的边界上每隔周长个像素提取一个点的坐标，求所提取四个点的平均坐标（即为小正方形的大致中心）

    int centerx=0,centery=0,n=contours[i].size();
    centerx = (contours[i][n/4].x + contours[i][n*2/4].x + contours[i][3*n/4].x + contours[i][n-1].x)/4;
    centery = (contours[i][n/4].y + contours[i][n*2/4].y + contours[i][3*n/4].y + contours[i][n-1].y)/4;
    Point point1=Point(centerx,centery);
    return point1;


}

int QrcodeDetect::calculateQrcodeLocation(vector<vector<Point> > contours, double &xOut,
                                          double &yOut, double &rOut)
{
    if(3!=contours.size())
    {
        xOut=0;
        yOut=0;
        rOut=0;
        return -1;
    }
    Point tmpPoint1=center_cal(contours,0);
    Point tmpPoint2=center_cal(contours,1);
    Point tmpPoint3=center_cal(contours,2);
    //找出顶点,根据斜边距离最长
    double l1,l2,l3;
    l1=(tmpPoint2.y-tmpPoint3.y)*(tmpPoint2.y-tmpPoint3.y)+(tmpPoint2.x-tmpPoint3.x)*(tmpPoint2.x-tmpPoint3.x);
    l2=(tmpPoint1.y-tmpPoint3.y)*(tmpPoint1.y-tmpPoint3.y)+(tmpPoint1.x-tmpPoint3.x)*(tmpPoint1.x-tmpPoint3.x);
    l3=(tmpPoint1.y-tmpPoint2.y)*(tmpPoint1.y-tmpPoint2.y)+(tmpPoint1.x-tmpPoint2.x)*(tmpPoint1.x-tmpPoint2.x);
    double cornerX,cornerY,centerX,centerY;
    if(l1>l2 && l1>l3)
    {
        cornerX=tmpPoint1.x;
        cornerY=tmpPoint1.y;
        centerX=(tmpPoint2.x+tmpPoint3.x)/2;
        centerY=(tmpPoint2.y+tmpPoint3.y)/2;
    }
    else if(l2>l1 && l2>l3)
    {
        cornerX=tmpPoint2.x;
        cornerY=tmpPoint2.y;
        centerX=(tmpPoint1.x+tmpPoint3.x)/2;
        centerY=(tmpPoint1.y+tmpPoint3.y)/2;
    }
    else
    {
        cornerX=tmpPoint3.x;
        cornerY=tmpPoint3.y;
        centerX=(tmpPoint1.x+tmpPoint2.x)/2;
        centerY=(tmpPoint1.y+tmpPoint2.y)/2;
    }

    xOut=centerX;
    yOut=centerY;
    rOut=atan2(cornerY-centerY,cornerX-centerX);
    qDebug()<<"xOut="<<xOut<<"yOut="<<yOut<<"rOut="<<rOut*180/M_PI;
    return 1;

}

